home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Frameworks / Grant's CGI Framework 1.0b14 / Util / CGI.c next >
Text File  |  1996-04-12  |  49KB  |  1,763 lines

  1. /*****
  2.  *
  3.  *    Grant's CGI Framework
  4.  *        (Common Grant Interface :-)
  5.  *        by Grant Neufeld
  6.  *        http://arpp.carleton.ca/grant/mac/grantscgi/
  7.  *
  8.  *    CGI.c
  9.  *
  10.  *    Standard functions for cgi applications.
  11.  *
  12.  *    You must call InitCGIUtil in your application startup.
  13.  *    You must install CGIAEHandle as the event handler for the WWWΩsdoc apple event
  14.  *    You must write the function:
  15.  *        void CustomCGIProcess ( CGIHdl theCGIHdl )
  16.  *        which is where you will, guess what, do your application specific processing
  17.  *        of the cgi stuff.
  18.  *
  19.  *    Do not call any functions begining with lower case 'cgi' - you can use any of the
  20.  *    others - but read their comments first for details.
  21.  *
  22.  *    watch the homepage for future upgrades
  23.  *
  24.  *
  25.  *    Copyright ©1995,1996 by Grant Neufeld
  26.  *
  27.  *    http://arpp.carleton.ca/grant/
  28.  *    gneufeld@ccs.carleton.ca
  29.  *    grant@acm.org
  30.  *  grant@kagi.com
  31.  *
  32.  *    This source may be freely used as long as the copyright notice is kept in the source.
  33.  *    I ask that you let me know of any enhancements (read: bug fixes) to this code.
  34.  *    I would also like copies of (or discounts on) anything you produce this with, please.
  35.  *
  36.  *    See the License and Limited Warranty Agreement for all the legal stuff.
  37.  *
  38.  *****/
  39.  
  40. #include "MyConfiguration.h"
  41. #if kCompileWithCGICode
  42.  
  43. #include <stdio.h>
  44. #include <string.h>
  45. #if __profile__ && __MWERKS__
  46. #include <Profiler.h>
  47. #endif
  48.  
  49. #include "compiler_stuff.h"
  50. #include "globals.h"
  51.  
  52. #include "AEFunc.h"
  53. #include "DebugUtil.h"
  54. #include "LogUtil.h"
  55. #include "MemoryUtil.h"
  56. #include "ProcessUtil.h"
  57. #include "Quit.h"
  58. #include "StringUtil.h"
  59.  
  60. /* CGI.h processes differently for CGI.c, this is controlled by defining __CGISegment__ */
  61. #define __CGISegment__    1
  62. #include "CGI.h"
  63. #undef    __CGISegment__
  64.  
  65.  
  66. /***  CONSTANT DECLARATIONS  ***/
  67.  
  68. #define kHTTPHeaderStrs        3000
  69. #define kHTTPHeaderOK        1
  70. #define kHTTPHeaderRedirect    2
  71. #define kHTTPHeaderErr        3
  72. #define kHTTPHeaderPush        4
  73.  
  74.  
  75. /***  LOCAL VARIABLES  ***/
  76.  
  77. #if kCompileWithThreadedAppleEvents
  78. static AEEventHandlerUPP    vCGIAEResumeCompleteUPP;
  79. #endif
  80.  
  81. static char *                vNULLString;
  82.  
  83.  
  84. /***  LOCAL FUNCTION PROTOTYPES  ***/
  85.  
  86. static    void    cgiDisposeHandle            ( CGIHdl );
  87.  
  88. static OSErr    cgiAESearchDocProcess        ( CGIHdl );
  89. static OSErr    cgiAEComplete                ( CGIHdl );
  90.  
  91. #if kCompileWithThreadedAppleEvents
  92. pascal void *    CGIAESearchDocProcessThread    ( void * );
  93. pascal OSErr    CGIAEResumeComplete            ( const AppleEvent *, AppleEvent *, long );
  94. #endif
  95.  
  96. #if kCompileWithCGImethod
  97. static    OSErr    cgiAEGetParamHTTPMethod        ( const AppleEvent *, AEKeyword, HTTPMethod *, char *, long );
  98. #endif
  99.  
  100. static void        cgiPostProcess                ( CGIHdl );
  101.  
  102.  
  103. /***  FUNCTIONS  ***/
  104.  
  105. /* This initialization function MUST be called in the startup sequence of your
  106.     application if you use any of the CGI functions (including the AppleEvent) */
  107. OSErr
  108. InitCGIUtil ( void )
  109. {
  110.     OSErr                theErr;
  111.     AEEventHandlerUPP    theUPP;
  112.     
  113.     /* initialize the null string to consist of one character '\0' */
  114.     vNULLString = (char *) MemoryNewPtr ( 1, &theErr );
  115.     if ( vNULLString != NULL )
  116.     {
  117.         vNULLString[0] = nil;
  118.     }
  119.     else
  120.     {
  121.         /* IMPORTANT: we failed initialization
  122.             if we couldn't allocate a one byte string! */
  123.         if ( theErr == noErr )
  124.         {
  125.             /* make sure there's an error value */
  126.             theErr = memFullErr;
  127.         }
  128.         
  129.         return theErr;
  130.     }
  131.     
  132.     GetIndString ( gHTTPHeaderOK, kHTTPHeaderStrs, kHTTPHeaderOK );
  133.     if ( gHTTPHeaderOK[nil] != nil )
  134.     {
  135.         P2CStr ( gHTTPHeaderOK );
  136.         gHTTPHeaderOKSize = strlen ( (char *)gHTTPHeaderOK );
  137.     }
  138.     
  139.     GetIndString ( gHTTPHeaderRedirect, kHTTPHeaderStrs, kHTTPHeaderRedirect );
  140.     if ( gHTTPHeaderRedirect[nil] != nil )
  141.     {
  142.         P2CStr ( gHTTPHeaderRedirect );
  143.         gHTTPHeaderRedirectSize = strlen ( (char *)gHTTPHeaderRedirect );
  144.     }
  145.     
  146.     GetIndString ( gHTTPHeaderErr, kHTTPHeaderStrs, kHTTPHeaderErr );
  147.     if ( gHTTPHeaderErr[nil] != nil )
  148.     {
  149.         P2CStr ( gHTTPHeaderErr );
  150.         gHTTPHeaderErrSize = strlen ( (char *)gHTTPHeaderErr );
  151.     }
  152.     
  153.     #if kCompileWithCGISendPartial
  154.     GetIndString ( gHTTPHeaderPush, kHTTPHeaderStrs, kHTTPHeaderPush );
  155.     if ( gHTTPHeaderPush[nil] != nil )
  156.     {
  157.         P2CStr ( gHTTPHeaderPush );
  158.         gHTTPHeaderPushSize = strlen ( (char *)gHTTPHeaderPush );
  159.     }
  160.     #endif
  161.     
  162.     /* it's okay to 'lose track' of theUPP since we never want to get rid of it
  163.         until the application quits - at which point it will automatically be
  164.         disposed of, anyway. */
  165.     theUPP = NewAEEventHandlerProc ( CGIAESearchDoc );
  166.     theErr = AEInstallEventHandler ( kAEClassCGI, kAEIDSearchDoc, theUPP, 0L, false );
  167.     
  168.     #if kCompileWithThreadedAppleEvents
  169.     vCGIAEResumeCompleteUPP = NewAEEventHandlerProc ( CGIAEResumeComplete );
  170.     #endif
  171.     
  172.     return theErr;
  173. } /* InitCGIUtil */
  174.  
  175.  
  176. /**  FORM FIELDS  **/
  177. #pragma mark -
  178. #if kCompileWithCGIFormHandling
  179.  
  180. /* The separator '&' separates individual fields.
  181.     The delimiter '=' delimits the name and value in a field.
  182.     For example: "Field 1=some stuff&Another Field=more stuff&Last Field=no stuff"
  183.     Means that there are 3 fields with names "Field 1", "Another Field" and "Last Field"
  184.     
  185.     The function returns an array of field records with the last containing null values.
  186.     
  187.     You generally shouldn't call this function outside this file.
  188.     Use it at your own risk. */
  189.     /* ••• I should add error reporting */
  190. p_export
  191. CGIFormField *
  192. CGIFormFieldsFromArgs ( char *theString, long *count, short *outErr )
  193. {
  194.     CGIFormField *    theFields;
  195.     long            totalStrSize;
  196.     long            totalFields;
  197.     long            nameSize;
  198.     long            valueSize;
  199.     long            currentField;
  200.     char *            theStringPtr;
  201.     char *            fieldSeparator;
  202.     char *            fieldDelimiter;
  203.     
  204.     my_assert ( theString != NULL, "\pCGIFormArgs: nil string" );
  205.     
  206.     theFields = NULL;
  207.     
  208.     /* don't return number of fields until function is successful */
  209.     *count = nil;
  210.     
  211.     totalStrSize = strlen ( theString );
  212.     
  213.     /* the total number of fields is the number of separator characters + 1 */
  214.     totalFields = StringCountChar ( theString, kCGIFormFieldSeparator ) + 1;
  215.     if ( totalFields == 1 )
  216.     {
  217.         /* the case where there were no separator characters is special,
  218.             test for a field delimiter to confirm that the string passed does
  219.             indeed contain field information */
  220.         fieldDelimiter = StringChar ( theString, kCGIFormFieldDelimiter );
  221.         if ( fieldDelimiter == NULL )
  222.         {
  223.             /* string does not contain field data */
  224.             *outErr = 1; /* • might need better error here */
  225.             
  226.             goto Exit_Fail;
  227.         }
  228.     }
  229.     
  230.     theFields = (CGIFormField *) MemoryNewPtr ( ((totalFields + 1) * sizeof(CGIFormField)), outErr );
  231.     if ( theFields == NULL )
  232.     {
  233.         /* memory didn't allocate */
  234.         goto Exit_Fail;
  235.     }
  236.     
  237.     /* set the name and value of the last field in the array to nil */
  238.     (theFields[totalFields]).name  = NULL;
  239.     (theFields[totalFields]).value = NULL;
  240.     
  241.     theStringPtr = theString;
  242.     
  243.     for ( currentField = nil; currentField < totalFields; currentField++ )
  244.     {
  245.         /* set the name and value of the current field in the array to nil.
  246.             this is to handle errors. */
  247.         (theFields[currentField]).name  = NULL;
  248.         (theFields[currentField]).value = NULL;
  249.         
  250.         fieldDelimiter = StringChar ( theStringPtr, kCGIFormFieldDelimiter );
  251.         fieldSeparator = StringChar ( theStringPtr, kCGIFormFieldSeparator );
  252.         
  253.         /* if there is a field delimiter, and it is before any field separator */
  254.         if ( (fieldDelimiter != NULL) && ((fieldSeparator > fieldDelimiter) ||
  255.             (fieldSeparator == NULL)) )
  256.         {
  257.             /* field name */
  258.             /* the size of the name string is the difference between the begining
  259.                 of the field and the position of the field delimiter */
  260.             nameSize = fieldDelimiter - theStringPtr;
  261.             
  262.             /* allocate the name string */
  263.             (theFields[currentField]).name = (char *) MemoryNewPtr ( nameSize + 1, outErr );
  264.             if ( (theFields[currentField]).name == NULL )
  265.             {
  266.                 /* memory didn't allocate */
  267.                 (theFields[currentField]).value = NULL;
  268.                 
  269.                 goto Exit_Fail;
  270.             }
  271.             
  272.             /* copy the field name */
  273.             BlockMove ( theStringPtr, (theFields[currentField]).name, nameSize );
  274.             /* null terminate the end of the name string */
  275.             ((theFields[currentField]).name)[nameSize] = nil;
  276.             /* convert the url encoded text to a normal string */
  277.             CGIDecodeURLChars ( (theFields[currentField]).name );
  278.             
  279.             /* field value */
  280.             if ( fieldSeparator != NULL )
  281.             {
  282.                 valueSize = fieldSeparator - (fieldDelimiter + 1);
  283.             }
  284.             else
  285.             {
  286.                 valueSize = strlen ( fieldDelimiter + 1 );
  287.             }
  288.             
  289.             (theFields[currentField]).value = (char *) MemoryNewPtr ( (valueSize + 1), outErr );
  290.             if ( (theFields[currentField]).value == NULL )
  291.             {
  292.                 /* memory didn't allocate */
  293.                 DisposePtr ( (theFields[currentField]).name );
  294.                 (theFields[currentField]).name = NULL;
  295.                 
  296.                 goto Exit_Fail;
  297.             }
  298.             
  299.             BlockMove ( fieldDelimiter + 1, (theFields[currentField]).value, valueSize );
  300.             ((theFields[currentField]).value)[valueSize] = nil;
  301.             CGIDecodeURLChars ( (theFields[currentField]).value );
  302.             
  303.             theStringPtr = fieldSeparator + 1;
  304.         }
  305.         else
  306.         {
  307.             /* invalid data encountered */
  308.             *outErr = 2; /* •• need better error value */
  309.             
  310.             goto Exit_Fail;
  311.         }
  312.     }
  313.     
  314.     /* assign the return parameters values */
  315.     *count    = totalFields;
  316.     *outErr    = noErr;
  317.     
  318.     return theFields;
  319.     
  320.     
  321.     Exit_Fail:
  322.     
  323.     if ( theFields != NULL )
  324.     {
  325.         /* release allocated memory */
  326.         CGIFormFieldsDispose ( theFields );
  327.     }
  328.     
  329.     return NULL;
  330. } /* CGIFormFieldsFromArgs */
  331.  
  332.  
  333. /* Returns a pointer to the first form field record, in the given fieldArray,
  334.     that's name matches the supplied field name.
  335.     [modified for 1.0b4 - use CGIHdl as parameter instead of CGIFormField] */
  336. p_export
  337. CGIFormField *
  338. CGIFormFieldsFindRecord ( CGIHdl theCGIHdl, const char *fieldName )
  339. {
  340.     CGIFormField    *fieldArray;
  341.     long            currentField;
  342.     short            stringDifference;
  343.     
  344.     my_assert ( theCGIHdl != NULL, "\pCGIFormFieldsFindRecord: theCGIHdl is NULL" );
  345.     my_assert ( fieldName != NULL, "\pCGIFormFieldsFindRecord: fieldName is NULL" );
  346.     
  347.     fieldArray = (*theCGIHdl)->formFields;
  348.     if ( (*theCGIHdl)->formFields == NULL )
  349.     {
  350.         return NULL;
  351.     }
  352.     
  353.     /* look til we find something or we hit the end */
  354.     for ( currentField = nil; (fieldArray[currentField]).name != NULL; currentField++ )
  355.     {
  356.         stringDifference = strcmp ( (fieldArray[currentField]).name, fieldName );
  357.         if ( stringDifference == nil )
  358.         {
  359.             /* found a match, so we're done */
  360.             return &(fieldArray[currentField]);
  361.         }
  362.     }
  363.     
  364.     /* didn't find a match */
  365.     return NULL;
  366. } /* CGIFormFieldsFindRecord */
  367.  
  368.  
  369. /* Returns a pointer to the string containing the value from the field specified
  370.     by 'fieldName'. Returns a pointer to a null string ("") if the field is not
  371.     found. The pointer points directly into the form fields array in theCGIHdl.
  372.     IMPORTANT: Whatever you do - do not attempt to deallocate the string returned
  373.     by this function!!! */
  374. p_export
  375. const char *
  376. CGIFormFieldsFindValue ( CGIHdl theCGIHdl, const char *fieldName )
  377. {
  378.     CGIFormField *    theField;
  379.     
  380.     my_assert ( theCGIHdl != NULL, "\pCGIFormFieldsFindValue: theCGIHdl is NULL" );
  381.     my_assert ( fieldName != NULL, "\pCGIFormFieldsFindValue: fieldName is NULL" );
  382.     
  383.     theField = CGIFormFieldsFindRecord ( theCGIHdl, fieldName );
  384.     if ( (theField == NULL) || (theField->value == NULL) )
  385.     {
  386.         return vNULLString;
  387.     }
  388.     else
  389.     {
  390.         return theField->value;
  391.     }
  392. } /* CGIFormFieldsFindValue */
  393.  
  394.  
  395. /* Deallocate memory for theFields array.
  396.     You generally shouldn't call this function outside this file.
  397.     Use it at your own risk. */
  398. void
  399. CGIFormFieldsDispose ( CGIFormField *theFields )
  400. {
  401.     long    offset;
  402.     
  403.     my_assert ( theFields != NULL, "\pCGIFormFieldsDispose: null field array pointer" );
  404.     
  405.     offset = nil;
  406.     
  407.     do
  408.     {
  409.         if ( (theFields[offset]).name != NULL )
  410.         {
  411.             /* if there's a name string, deallocate its memory */
  412.             DisposePtr ( (Ptr)((theFields[offset]).name) );
  413.             
  414.             if ( (theFields[offset]).value != NULL )
  415.             {
  416.                 /* if there's a value string, deallocate its memory */
  417.                 DisposePtr ( (Ptr)((theFields[offset]).value) );
  418.             }
  419.         }
  420.         
  421.         offset++;
  422.     } while ( (theFields[offset]).name != NULL );
  423.     
  424.     DisposePtr ( (Ptr)theFields );
  425. } /* CGIFormFieldsDispose */
  426.  
  427. #endif    /* kCompileWithCGIFormHandling */
  428.  
  429.  
  430. /**  ACTION SUPPORT  **/
  431. #pragma mark -
  432. #if kCompileWithCGIActionSupport
  433.  
  434. /* Returns true if the action parameter is either CGI or ACGI.
  435.     Returns false if it is some other action. */
  436. p_export
  437. Boolean
  438. CGIActionIsCGIorACGI ( CGIHdl theCGIHdl )
  439. {
  440.     int        stringDifference;
  441.     
  442.     my_assert ( theCGIHdl != NULL, "\pCGIActionIsCGIorACGI: theCGIHdl is NULL" );
  443.     
  444.     /* is the action "CGI"? */
  445.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameCGI );
  446.     if ( stringDifference == nil )
  447.     {
  448.         return true;
  449.     }
  450.     
  451.     /* is the action "ACGI"? */
  452.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameACGI );
  453.     if ( stringDifference == nil )
  454.     {
  455.         return true;
  456.     }
  457.     
  458.     /* didn't find a match */
  459.     return false;
  460. } /* CGIActionIsCGIorACGI */
  461.  
  462. #endif /* kCompileWithCGIActionSupport */
  463.  
  464.  
  465. /**  CHARACTER CODING  **/
  466. #pragma mark -
  467.  
  468. /* replaces instances of percent signs (%) followed by an ASCII char value
  469.     with the actual character.
  470.     This function modifies theString parameter! */
  471. p_export
  472. void
  473. CGIDecodeURLChars ( char *theString )
  474. {
  475.     UInt32            read;
  476.     UInt32            write;
  477.     unsigned char    theChar;
  478.     unsigned char    highOrder;
  479.     unsigned char    lowOrder;
  480.     Boolean            isValid;
  481.     
  482.     my_assert ( theString != NULL, "\pCGIDecodeURLChars: NULL string" );
  483.     
  484.     read    = nil;
  485.     write    = nil;
  486.     
  487.     while ( theString[read] != nil )
  488.     {
  489.         switch ( theString[read] )
  490.         {
  491.             case '%':
  492.                 /* a percent symbol begins a hex char block (%## where ## is the hex value) */
  493.                 
  494.                 isValid = true;
  495.                 
  496.                 /* determine high order hex character */
  497.                 if ( (theString[read+1] >= 'A') && (theString[read+1] <= 'F') )
  498.                 {
  499.                     /* uppercase A-F convert to 10-15 */
  500.                     highOrder = theString[read+1] - 'A' + 10;
  501.                 }
  502.                 else if ( (theString[read+1] >= 'a') && (theString[read+1] <= 'f') )
  503.                 {
  504.                     /* lowercase a-f convert to 10-15 */
  505.                     highOrder = theString[read+1] - 'a' + 10;
  506.                 }
  507.                 else if ( (theString[read+1] >= '0') && (theString[read+1] <= '9') )
  508.                 {
  509.                     /* character digits 0-9 convert to decimal 0-9 */
  510.                     highOrder = theString[read+1] - '0';
  511.                 }
  512.                 else
  513.                 {
  514.                     /* Illegal character! Can't convert from hex */
  515.                     isValid = false;
  516.                 }
  517.                 
  518.                 /* Multiply high order hex digit by 16 */
  519.                 highOrder *= 16;
  520.                 
  521.                 /* determine low order hex character */
  522.                 if ( (theString[read+2] >= 'A') && (theString[read+2] <= 'F') )
  523.                 {
  524.                     /* uppercase A-F convert to 10-15 */
  525.                     lowOrder = (theString[read+2] - 'A' + 10);
  526.                 }
  527.                 else if ( (theString[read+2] >= 'a') && (theString[read+2] <= 'f') )
  528.                 {
  529.                     /* lowercase a-f convert to 10-15 */
  530.                     lowOrder = (theString[read+2] - 'a' + 10);
  531.                 }
  532.                 else if ( (theString[read+2] >= '0') && (theString[read+2] <= '9') )
  533.                 {
  534.                     /* character digits 0-9 convert to decimal 0-9 */
  535.                     lowOrder = (theString[read+2] - '0');
  536.                 }
  537.                 else
  538.                 {
  539.                     /* Illegal character! Can't convert from hex */
  540.                     isValid = false;
  541.                 }
  542.                 
  543.                 theChar = highOrder + lowOrder;
  544.                 
  545.                 if ( isValid )
  546.                 {
  547.                     isValid = (theChar >= 0) && (theChar < 256);
  548.                 }
  549.                 
  550.                 if ( isValid )
  551.                 {
  552.                     /* if theChar is valid, write it out */
  553.                     
  554.                     if ( theChar == 10 )
  555.                     {
  556.                         /* don't write newline */
  557.                         write--;
  558.                     }
  559.                     else
  560.                     {
  561.                         theString[write] = theChar;
  562.                     }
  563.                     
  564.                     /* Increment read past the two digits of the hex code */
  565.                     read += 2;
  566.                 }
  567.                 else
  568.                 {
  569.                     /* invalid hex character code, just write out the percent symbol */
  570.                     theString[write] = theString[read];
  571.                 }
  572.                 break;
  573.             
  574.             case '+':
  575.                 /* Plus symbols convert to space */
  576.                 theString[write] = ' ';
  577.                 break;
  578.                 
  579.             case 10:
  580.                 /* ignore line feeds, we only need carriage returns (13) */
  581.                 write--;
  582.                 break;
  583.                 
  584.             default:
  585.                 /* write out the character */
  586.                 theString[write] = theString[read];
  587.                 break;
  588.         }
  589.             
  590.         read++;
  591.         write++;
  592.     }
  593.     
  594.     /* terminate the string */
  595.     theString[write] = '\0';
  596. } /* CGIDecodeURLChars */
  597.  
  598.  
  599. /* • UPDATE 1996-03-10 •
  600.     %hex encode all non-alphanumeric characters. IE. '~' (126) becomes '%7E'
  601.     theString parameter is not modified.
  602.     Will return NULL if unable to allocate memory for the encoded string.
  603.     outErr will not be set if it is a NULL ptr. */
  604. p_export
  605. char *
  606. CGIEncodeURLChars ( const char *theString, OSErr *outErr )
  607. {
  608.     char *            theResult;    /* new string containing the encoded form of theString */
  609.     long            strSize;    /* size of the source string */
  610.     Boolean            charNeedsToBeHex;    /* char needs to be encoded as hex */
  611.     const char *    stringLoc;    /* current location in theString */
  612.     char *            resultLoc;    /* current location in theResult string */
  613.     
  614.     my_assert ( theString != NULL, "\p: theString is NULL" );
  615.     
  616.     /* loop through the string finding out how big our final string will need to be.
  617.         Add two extra bytes for every 'special' character. */
  618.     strSize = nil;
  619.     for ( stringLoc = theString; *stringLoc != nil; stringLoc++ )
  620.     {
  621.         charNeedsToBeHex = CGICharWillHex ( *stringLoc );
  622.         if ( charNeedsToBeHex )
  623.         {
  624.             /* the character at strOffset is alpha-numeric */
  625.             strSize++;
  626.         }
  627.         else
  628.         {
  629.             /* the character at strOffset is special (not alpha-numeric) */
  630.             strSize += 3;
  631.         }
  632.     }
  633.     
  634.     /* allocate space for the new encoded string */
  635.     theResult = (char *) MemoryNewPtr ( (strSize + 1), outErr );
  636.     if ( theResult != NULL )
  637.     {
  638.         /* encode theString into the new encoded string (theResult).
  639.             loop through the characters of the old string,
  640.             copying the alpha-numeric chars to the new string,
  641.             but copying in %XX encoded form when non-alpha-numeric. */
  642.         for ( stringLoc = theString, resultLoc = theResult; *stringLoc != nil; stringLoc++, resultLoc++ )
  643.         {
  644.             charNeedsToBeHex = CGICharWillHex ( *stringLoc );
  645.             if ( charNeedsToBeHex )
  646.             {
  647.                 /* if the character is alphanumeric just copy it */
  648.                 *resultLoc = *stringLoc;
  649.             }
  650.             else
  651.             {
  652.                 /* if the character is not alphanumeric, hex encode it */
  653.                 CGICharToHex ( *stringLoc, resultLoc );
  654.                 /* add the extra two characters for hex encoding to the result offset */
  655.                 resultLoc += 2;
  656.             }
  657.         }
  658.         
  659.         /* terminate the string after the last character written */
  660.         *resultLoc = nil;
  661.     }
  662.     
  663.     return theResult;
  664. } /* CGIEncodeURLChars */
  665.  
  666.  
  667. /* returns true if theChar will be converted to hex when encoding as an URL string */
  668. p_export
  669. Boolean
  670. CGICharWillHex ( unsigned char theChar )
  671. {
  672.     return (
  673.         ((theChar >= 'a')  && (theChar <= 'z')) ||  /* 0x60 - 0x7A;       a-z   */
  674.         ((theChar >= '?')  && (theChar <= 'Z')) ||  /* 0x3F - 0x5A; ? @   A-Z   */
  675.         ((theChar >= '-')  && (theChar <= ':')) ||  /* 0x2D - 0x3A; - . / 0-9 : */
  676.         ((theChar >= '\"') && (theChar <= '$')) ||  /* 0x22 - 0x24; " # $       */
  677.          (theChar == '&')  || (theChar == '\'') ||  /* 0x26   0x27; & '         */
  678.          (theChar == '=') );                         /* 0x3D;        =           */
  679. } /* CGICharWillHex */
  680.  
  681.  
  682. /* Converts theChar to percent-hex encoding for URLs, and writes that to theString.
  683.     This assumes that theString is at least 4 bytes long (to have enough room to
  684.     write the hex encoded text). */
  685. p_export
  686. void
  687. CGICharToHex ( unsigned char theChar, char *theString )
  688. {
  689.     unsigned char    chrChunk;
  690.     
  691.     /* start off with a percent symbol to indicate a hex character code */
  692.     theString[0] = '%';
  693.     
  694.     /* mask to get the high 4 bits, then shift them into the lower 4 bits */
  695.     chrChunk = (theChar & 0xF0) >> 4;
  696.     if ( chrChunk > 9 )
  697.     {
  698.         /* chrChunk is a value between A and F in hex */
  699.         theString[1] = (chrChunk - 10) + 'A';
  700.     }
  701.     else
  702.     {
  703.         /* chrChunk is a value between 0 and 9 in hex */
  704.         theString[1] = chrChunk + '0';
  705.     }
  706.     
  707.     /* mask to get the low 4 bits */
  708.     chrChunk = theChar & 0x0F;
  709.     if ( chrChunk > 9 )
  710.     {
  711.         /* chrChunk is a value between A and F in hex */
  712.         theString[2] = (chrChunk - 10) + 'A';
  713.     }
  714.     else
  715.     {
  716.         /* chrChunk is a value between 0 and 9 in hex */
  717.         theString[2] = chrChunk + '0';
  718.     }
  719. } /* CGICharToHex */
  720.  
  721.  
  722. /* convert a '/' delimited path to a ':' delimited one, and make sure there's
  723.     one ':' at the start if the path includes folders.
  724.     This function may modify ioPathString.
  725.     ioPathString MUST have an extra byte after its terminating null byte. */
  726. p_export
  727. void
  728. CGIPathToMacPath ( char *ioPathString )
  729. {
  730.     char *    theWorkString;
  731.     char *    theSlashOffset;
  732.     
  733.     /* we start with a directory marker, so we won't need to add one. */
  734.     if ( (ioPathString[0] == '/') || (ioPathString[0] == ':') )
  735.     {
  736.         StringConvertCharToChar ( ioPathString, '/', ':' );
  737.     }
  738.     else
  739.     {
  740.         /* check if there are any slashes */
  741.         theSlashOffset = strchr ( ioPathString, '/' );
  742.         if ( theSlashOffset == NULL )
  743.         {
  744.             /* there are no slashes, so nothing no change is needed. */
  745.         }
  746.         else
  747.         {
  748.             /* find the terminating null byte at the end of the string */
  749.             theWorkString = strchr ( ioPathString, '\0' );
  750.             while ( theWorkString >= ioPathString )
  751.             {
  752.                 /* the char following the current one is written as a colon if
  753.                     this one is a slash, or has this char written to it */
  754.                 theWorkString[1] = (theWorkString[0] == '/')? ':' : theWorkString[0];
  755.                 theWorkString--;
  756.             }
  757.             
  758.             /* make sure there is a leading colon for the path */
  759.             ioPathString[0] = ':';
  760.         }
  761.     }
  762. } /* CGIPathToMacPath */
  763.  
  764.  
  765. /**  Memory Allocation Cleanup  **/
  766. #pragma mark -
  767.  
  768. /*  */
  769. static void
  770. cgiDisposeHandle ( CGIHdl theCGIHdl )
  771. {
  772.     my_assert ( theCGIHdl != NULL, "\pcgiDisposeHandle: theCGIHdl is NULL" );
  773.     my_assert ( *theCGIHdl != NULL, "\pcgiDisposeHandle: *theCGIHdl is NULL" );
  774.     
  775.     /* the following is a bunch of statements checking if a parameter has been
  776.         allocated and disposing of it if it has */
  777.     
  778.     #if kCompileWithCGIpath_args
  779.     if ( (*theCGIHdl)->path_args != NULL )
  780.     {
  781.         DisposePtr ( (Ptr)((*theCGIHdl)->path_args) );
  782.     }
  783.     #endif
  784.     #if kCompileWithCGIhttp_search_args
  785.     if ( (*theCGIHdl)->http_search_args != NULL )
  786.     {
  787.         DisposePtr ( (Ptr)((*theCGIHdl)->http_search_args) );
  788.     }
  789.     #endif
  790.     #if kCompileWithCGIpost_args
  791.     if ( (*theCGIHdl)->post_args != NULL )
  792.     {
  793.         DisposePtr ( (Ptr)((*theCGIHdl)->post_args) );
  794.     }
  795.     #endif
  796.     #if kCompileWithCGIscript_name
  797.     if ( (*theCGIHdl)->script_name != NULL )
  798.     {
  799.         DisposePtr ( (Ptr)((*theCGIHdl)->script_name) );
  800.     }
  801.     #endif
  802.     #if kCompileWithCGIreferer
  803.     if ( (*theCGIHdl)->referer != NULL )
  804.     {
  805.         DisposePtr ( (Ptr)((*theCGIHdl)->referer) );
  806.     }
  807.     #endif
  808.     
  809.     #if kCompileWithCGIActionSupport
  810.     if ( (*theCGIHdl)->action_path != NULL )
  811.     {
  812.         DisposePtr ( (Ptr)((*theCGIHdl)->action_path) );
  813.     }
  814.     #endif
  815.     
  816.     #if kCompileWithCGIfull_request
  817.     if ( (*theCGIHdl)->full_request != NULL )
  818.     {
  819.         DisposePtr ( (Ptr)((*theCGIHdl)->full_request) );
  820.     }
  821.     #endif
  822.     
  823.     #if kCompileWithCGIFormHandling
  824.     if ( (*theCGIHdl)->formFields != NULL )
  825.     {
  826.         CGIFormFieldsDispose ( (*theCGIHdl)->formFields );
  827.     }
  828.     #endif
  829.     
  830.     if ( (*theCGIHdl)->responseData != NULL )
  831.     {
  832.         #if kCompileWithCGIResponseDataAsHandle
  833.             DisposeHandle ( (*theCGIHdl)->responseData );
  834.         #else
  835.             DisposePtr ( (Ptr)((*theCGIHdl)->responseData) );
  836.         #endif
  837.     }
  838.     
  839.     DisposeHandle ( (Handle)theCGIHdl );
  840. } /* cgiDisposeHandle */
  841.  
  842.  
  843. /**  APPLE EVENT SUPPORT  **/
  844. #pragma mark -
  845.  
  846. /* AppleEvent Handler for the CGI WWWΩ-sdoc event */
  847. pascal OSErr
  848. CGIAESearchDoc ( AppleEvent *theAppleEvent, AppleEvent *theReply, long Reference )
  849. {
  850.     OSErr        theErr;
  851.     CGIHdl        theCGIHdl;
  852.     #if kCompileWithThreadedAppleEvents
  853.     ThreadID    theThread;
  854.     #endif
  855.     #if kCompileWithDebugLogging
  856.     char        tempStr[16];
  857.     #endif
  858.     
  859.     /* we're handling a CGI event, so we're more busy now */
  860.     ProcessIsMoreBusy ();
  861.     
  862.     /* reset 'quit on idle time' timer */
  863.     ResetQuitIdleTimer();
  864.     
  865.     #if kCompileWithDebugLogging
  866.     LogStringP ( "\p==================\rCGI Event Recieved\rTICKS: " );
  867.     sprintf ( tempStr, "%d", TickCount() );
  868.     LogStringBreak ( tempStr );
  869.     LogStringBreakP ( "\p------------------" );
  870.     #endif
  871.     
  872.     /* Allocate the CGIHdl data structure - zeroing out its contents. */
  873.     theCGIHdl = (CGIHdl) MemoryNewHandleClear ( sizeof(CGIrecord), &theErr );
  874.     if ( theCGIHdl == NULL )
  875.     {
  876.         /* memory didn't allocate - can't process cgi */
  877.         return theErr;
  878.     }
  879.     
  880.     /* store references to the apple event and reply records */
  881.     (*theCGIHdl)->appleEvent = *theAppleEvent;
  882.     (*theCGIHdl)->replyEvent = *theReply;
  883.     
  884. //    MoveHHi ( (Handle)theCGIHdl );
  885.     
  886.     #if kCompileWithThreadedAppleEvents
  887.     #if kCompileWithThreadsOptional
  888.     if ( gHasThreadMgr )
  889.     {
  890.     #endif
  891.         
  892.         /* It is necessary to suspend the AppleEvent in order to thread its
  893.             processing because of some real weirdness with AEProcessAppleEvent
  894.             not being "reentrant." This means you can't be processing multiple
  895.             Apple Events at the same time, so they have to be 'suspended' if
  896.             you want to deal with more than one (IE. multi-threaded processing). */
  897.         theErr = AESuspendTheCurrentEvent ( theAppleEvent );
  898.         if ( theErr == noErr)
  899.         {
  900.             (*theCGIHdl)->suspended = true;
  901.             /* AppleEvent has been suspended, so we can spawn a thread for processing. */
  902.             theErr = ThreadNewThreadFromPool ( CGIAESearchDocProcessThread,
  903.                 theCGIHdl, (void**)NULL, &theThread );
  904.         }
  905.         
  906.     #if kCompileWithThreadsOptional
  907.     }
  908.     #endif
  909.     #endif /* kCompileWithThreadedAppleEvents */
  910.     
  911.     #if kCompileWithThreadedAppleEvents
  912.     #if kCompileWithThreadsOptional
  913.     if ( !gHasThreadMgr || (theErr != noErr) )
  914.     #else
  915.     if ( theErr != noErr )
  916.     #endif
  917.     {
  918.     #endif
  919.         
  920.         /* if threading isn't available, or the attempt to thread failed,
  921.             or the attempt to suspend the AppleEvent failed,
  922.             process the Apple Event without threading. */ 
  923.         theErr = cgiAESearchDocProcess ( theCGIHdl );
  924.         
  925.     #if kCompileWithThreadedAppleEvents
  926.     }
  927.     else
  928.     {
  929.         /* we suspended the AE, and spawned the thread, now let's start it. */
  930. //•••        YieldToThread ( theThread );
  931.     }
  932.     #endif
  933.     
  934.     return theErr;
  935. } /* CGIAESearchDoc */
  936.  
  937.  
  938. /* Entry point for cgi handler thread. */
  939. #if kCompileWithThreadedAppleEvents
  940. pascal void *
  941. CGIAESearchDocProcessThread ( void *threadParam )
  942. {
  943.     OSErr        theErr;
  944.     ThreadID    currentThread;
  945.     
  946.     my_assert ( threadParam != NULL,
  947.         "\pCGIAESearchDocProcessThread: the CGI handle (threadParam) is NULL" );
  948.     
  949.     /* The threadParam is used to pass the CGIHdl. */
  950.     theErr = cgiAESearchDocProcess ( (CGIHdl)threadParam );
  951.     
  952.     /* Find the ID of current thread and use DisposeThread to dispose of it so
  953.         that my custom thread termination procedure will be used to recycle
  954.         this thread's allocation for the thread pool. */
  955.     GetCurrentThread ( ¤tThread );
  956.     DisposeThread ( currentThread, (void *)theErr, true );
  957.     
  958.     /* This line below is actually irrelevant, since the DisposeThread call above
  959.         will result in the immediate termination of this thread.
  960.         I keep it in because a return result is needed for the compiler not to
  961.         issue a warning (and I have the "treat all warnings as errors" flag set
  962.         in my compiler, like every programmer should). */
  963.     return (void *)theErr;
  964. } /* CGIAESearchDocProcessThread */
  965. #endif
  966.  
  967. /* Process the CGI WWWΩ-sdoc event.
  968.     theCGIHdl must be valid (non-nil) and unlocked.
  969.     Responsible for ensuring that cgiAEComplete is called so CGIDisposeHandle
  970.     will be called. */
  971. static OSErr
  972. cgiAESearchDocProcess ( CGIHdl theCGIHdl )
  973. {
  974.     OSErr        theErr;
  975.     Ptr            tempBuffer;
  976.     AppleEvent    theAppleEvent;
  977.     /* the 'fieldError' variable is only used if forms with auto-processing are on,
  978.         the method parameter is used, and one or both of the http_search_args and
  979.         post_args are used */
  980.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod && (kCompileWithCGIhttp_search_args || kCompileWithCGIpost_args)
  981.     short        fieldError;
  982.     #endif
  983.     
  984.     my_assert ( theCGIHdl != NULL, "\pcgiAESearchDocProcess: theCGIHdl is NULL" );
  985. //    my_assert ( (*theCGIHdl)->appleEvent != NULL, "\pcgiAESearchDocProcess: theAppleEvent is nil" );
  986.     
  987.     #if __profile__ && __MWERKS__
  988.     gProfileOn = !( ProfilerInit( collectDetailed, bestTimeBase, 20, 5 ) );
  989.     #endif
  990.     
  991.     /* reset 'quit on idle time' timer */
  992.     ResetQuitIdleTimer();
  993.         
  994.     tempBuffer = MemoryNewPtr ( kCGIParamMaxSize, &theErr );
  995.     if ( tempBuffer == NULL )
  996.     {
  997.         goto Exit_Complete;
  998.     }
  999.     
  1000.     my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
  1001.         "\pcgiAESearchDocProcess: theCGIHdl is already locked!" );
  1002.     HLockHi ( (Handle)theCGIHdl );
  1003.     
  1004.     /* copy the AppleEvent record into a local variable for faster access. */
  1005.     theAppleEvent = (*theCGIHdl)->appleEvent;
  1006.     
  1007.     /* the following section is where the parameters are pulled from the CGI
  1008.         Apple Event and allocated in the CGI Handle */
  1009.     
  1010.     /* '----' - direct parameter:
  1011.         path_args - arguments to the URL after a $ */
  1012.     #if kCompileWithCGIpath_args
  1013.     theErr = AEGetParamString ( &theAppleEvent, '----', &((*theCGIHdl)->path_args),
  1014.         (char *)tempBuffer, kCGIParamMaxSize );
  1015.     #if kCompileWithCGIAutoDecode
  1016.     if ( theErr == noErr )
  1017.     {
  1018.         CGIDecodeURLChars ( (*theCGIHdl)->path_args );
  1019.     }
  1020.     #endif
  1021.     #endif    /* kCompileWithCGIpath_args */
  1022.     
  1023.     /* 'kfor' - search arguments:
  1024.         http_search_args - arguments to the URL after a ? */
  1025.     #if kCompileWithCGIhttp_search_args
  1026.     theErr = AEGetParamString ( &theAppleEvent, kCGIhttp_search_args,
  1027.         &((*theCGIHdl)->http_search_args), (char *)tempBuffer, kCGIParamMaxSize );
  1028.     /* leave decoding to after parsing of form fields */
  1029.     #endif
  1030.     
  1031.     /* 'user' - user name:
  1032.         username - authenticated user name */
  1033.     #if kCompileWithCGIusername
  1034.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIusername,
  1035.         (*theCGIHdl)->username, kCGIMaxusername );
  1036.     #endif
  1037.     
  1038.     /* 'pass' - password:
  1039.         password - authenticated password */
  1040.     #if kCompileWithCGIpassword
  1041.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIpassword,
  1042.         (*theCGIHdl)->password, kCGIMaxpassword );
  1043.     #endif
  1044.     
  1045.     /* 'frmu' - from user:
  1046.         from_user - non-standard. e-mail address of remote user */
  1047.     #if kCompileWithCGIfrom_user
  1048.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIfrom_user,
  1049.         (*theCGIHdl)->from_user, kCGIMaxfrom_user );
  1050.     #endif
  1051.     
  1052.     /* 'addr' - client address:
  1053.         client_address - IP address or domain name of remote client's host */
  1054.     #if kCompileWithCGIclient_address
  1055.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIclient_address,
  1056.         (*theCGIHdl)->client_address, kCGIMaxclient_address );
  1057.     #endif
  1058.     
  1059.     /* 'post' - post arguments:
  1060.         post_args -  */
  1061.     #if kCompileWithCGIpost_args
  1062.     theErr = AEGetParamString ( &theAppleEvent, kCGIpost_args,
  1063.         &((*theCGIHdl)->post_args), (char *)tempBuffer, kCGIParamMaxSize );
  1064.     /* leave decoding to after form parsing */
  1065.     #endif
  1066.     
  1067.     /* 'meth' - HTTP method:
  1068.         method - GET, POST, etc. Used to tell if post_args are valid */
  1069.     #if kCompileWithCGImethod
  1070.     theErr = cgiAEGetParamHTTPMethod ( &theAppleEvent, kCGImethod,
  1071.         &((*theCGIHdl)->method), (char *)tempBuffer, kCGIParamMaxSize );
  1072.     #endif
  1073.     
  1074.     /* 'svnm' - server name:
  1075.         server_name - name or IP address of this server */
  1076.     #if kCompileWithCGIserver_name
  1077.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIserver_name,
  1078.         (*theCGIHdl)->server_name, kCGIMaxserver_name );
  1079.     #endif
  1080.     
  1081.     /* 'svpt' - server port:
  1082.         server_port - TCP/IP port number being used by this server */
  1083.     #if kCompileWithCGIserver_port
  1084.     theErr = AEGetParamShort ( &theAppleEvent, kCGIserver_port,
  1085.         &((*theCGIHdl)->server_port), (char *)tempBuffer, kCGIParamMaxSize );
  1086.     #endif
  1087.     
  1088.     /* 'scnm' - script name:
  1089.         script_name - URL name of this script */
  1090.     #if kCompileWithCGIscript_name
  1091.     theErr = AEGetParamString ( &theAppleEvent, kCGIscript_name,
  1092.         &((*theCGIHdl)->script_name), (char *)tempBuffer, kCGIParamMaxSize );
  1093.     #if kCompileWithCGIAutoDecode
  1094.     if ( theErr == noErr )
  1095.     {
  1096.         CGIDecodeURLChars ( (*theCGIHdl)->script_name );
  1097.     }
  1098.     #endif
  1099.     #endif
  1100.     
  1101.     /* 'ctyp' - content type:
  1102.         content_type - MIME content type of post_args */
  1103.     #if kCompileWithCGIcontent_type
  1104.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIcontent_type,
  1105.         (*theCGIHdl)->content_type, kCGIMaxcontent_type );
  1106.     #endif
  1107.     
  1108.     /* 'refr' - referer:
  1109.         referer - the URL of the page referencing this document */
  1110.     #if kCompileWithCGIreferer
  1111.     theErr = AEGetParamString ( &theAppleEvent, kCGIreferer,
  1112.         &((*theCGIHdl)->referer), (char *)tempBuffer, kCGIParamMaxSize );
  1113.     #endif
  1114.     
  1115.     /* 'Agnt' - user agent:
  1116.         user_agent - the name and version of the WWW client software being used */
  1117.     #if kCompileWithCGIuser_agent
  1118.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIuser_agent,
  1119.         (*theCGIHdl)->user_agent, kCGIMaxuser_agent );
  1120.     #endif
  1121.     
  1122.     /* 'Kact' - action name:
  1123.         action - the name of the action (CGI or ACGI if not a user defined action) */
  1124.     #if kCompileWithCGIActionSupport
  1125.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIaction,
  1126.         (*theCGIHdl)->action, kCGIMaxaction );
  1127.     #endif
  1128.     
  1129.     /* 'Kapt' - action path:
  1130.         action_path - path to the action application */
  1131.     #if kCompileWithCGIActionSupport
  1132.     theErr = AEGetParamString ( &theAppleEvent, kCGIaction_path,
  1133.         &((*theCGIHdl)->action_path), (char *)tempBuffer, kCGIParamMaxSize );
  1134.     #endif
  1135.     
  1136.     /* 'Kcip' - client IP address:
  1137.         client_ip - the IP address of the client */
  1138.     #if kCompileWithCGIclient_ip
  1139.     theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIclient_ip,
  1140.         (*theCGIHdl)->client_ip, kCGIMaxclient_ip );
  1141.     #endif
  1142.     
  1143.     /* 'Kfrq' - full request:
  1144.         full_request - the full text of the request */
  1145.     #if kCompileWithCGIfull_request
  1146.     theErr = AEGetParamString ( &theAppleEvent, kCGIfull_request,
  1147.         &((*theCGIHdl)->full_request), (char *)tempBuffer, kCGIParamMaxSize );
  1148.     #endif
  1149.     
  1150.     /* 'Kcid' - connection ID:
  1151.         connection - the ID of the server's connection with a client */
  1152.     #if kCompileWithCGISendPartial
  1153.     theErr = AEGetParamLong ( &theAppleEvent, kCGIconnection, &((*theCGIHdl)->connection) );
  1154.     #endif
  1155.     
  1156.     /* don't need the buffer any more */
  1157.     DisposePtr ( tempBuffer );
  1158.     
  1159. //•don't need to check for required parameters because Chuck Shotton says that all
  1160. //parameters are to be optional.
  1161. //    /* check that all required parameters were retreived */
  1162. //    theErr = AEFuncGotRequiredParams ( &theAppleEvent );
  1163.     
  1164.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod
  1165.     /* separate the form fields into an array */
  1166.     switch ( (*theCGIHdl)->method )
  1167.     {
  1168.         #if kCompileWithCGIhttp_search_args
  1169.         case HTTP_get :
  1170.             if ( (*theCGIHdl)->http_search_args != NULL )
  1171.             {
  1172.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs (
  1173.                     (*theCGIHdl)->http_search_args, &((*theCGIHdl)->totalFields),
  1174.                     &fieldError );
  1175.             }
  1176.             break;
  1177.         #endif
  1178.         
  1179.         #if kCompileWithCGIpost_args
  1180.         case HTTP_post :
  1181.             if ( (*theCGIHdl)->post_args != NULL )
  1182.             {
  1183.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs (
  1184.                     (*theCGIHdl)->post_args, &((*theCGIHdl)->totalFields), &fieldError );
  1185.             }
  1186.             break;
  1187.         #endif
  1188.     }
  1189.     #endif    /* kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod */
  1190.     
  1191.     /* now that the possible need to use them for form fields is over, we can
  1192.         decode the search args */
  1193.     #if kCompileWithCGIAutoDecode && kCompileWithCGIhttp_search_args
  1194.     if ( (*theCGIHdl)->http_search_args != NULL )
  1195.     {
  1196.         CGIDecodeURLChars ( (*theCGIHdl)->http_search_args );
  1197.     }
  1198.     #endif
  1199.     
  1200.     HUnlock ( (Handle)theCGIHdl );
  1201.     
  1202.     #if kCompileWithDebugLogging
  1203.     CGILogData ( theCGIHdl );
  1204.     #endif
  1205.     
  1206.     /* this is where the application specific cgi handling comes into play
  1207.         the function "CustomCGIProcess" must be provided by the user of this source code */
  1208.     CustomCGIProcess ( theCGIHdl );
  1209.     
  1210.     Exit_Complete:
  1211.     
  1212.     #if __profile__ && __MWERKS__
  1213.     if ( gProfileOn )
  1214.     {
  1215.         ProfilerDump ( "\p" kProfileNameStr "-" kProcessorString ".prof" );
  1216.         ProfilerTerm ();
  1217.     }
  1218.     #endif
  1219.     
  1220.     #if kCompileWithThreadedAppleEvents
  1221.     if ( (*theCGIHdl)->suspended )
  1222.     {
  1223.         /* We're in a suspended AppleEvent, so we'll need to resume the
  1224.             AppleEvent to have it complete and return the reply properly. */
  1225.         theErr = AEResumeTheCurrentEvent ( &theAppleEvent,
  1226.             &((*theCGIHdl)->replyEvent), vCGIAEResumeCompleteUPP, (long)theCGIHdl );
  1227.     }
  1228.     else
  1229.     {
  1230.     #endif
  1231.         
  1232.         /* We weren't suspended, but still need to take care of the AppleEvent
  1233.             reply record. */
  1234.         theErr = cgiAEComplete ( theCGIHdl );
  1235.         
  1236.     #if kCompileWithThreadedAppleEvents
  1237.     }
  1238.     #endif
  1239.     
  1240.     /* reset 'quit on idle time' timer */
  1241.     ResetQuitIdleTimer();
  1242.     
  1243.     return theErr;
  1244. } /* cgiAESearchDocProcess */
  1245.  
  1246.  
  1247. /* Call the event completion function (cgiAEComplete) when resuming
  1248.      suspended AppleEvents. theReference must be a CGIHdl.
  1249.      The result returned from this function will be used as the result of the AppleEvent. */
  1250. #if kCompileWithThreadedAppleEvents
  1251. pascal OSErr
  1252. CGIAEResumeComplete ( const AppleEvent *theAppleEvent, AppleEvent *theReply, long theReference )
  1253. {
  1254.     OSErr    theErr;
  1255.     
  1256.     my_assert ( theReference != nil,
  1257.         "\pCGIAEResumeComplete: the CGI handle (theReference) is nil" );
  1258.     
  1259.     theErr = cgiAEComplete ( (CGIHdl)theReference );
  1260.         
  1261.     return theErr;
  1262. } /* CGIAEResumeComplete */
  1263. #endif
  1264.  
  1265. /* Complete the CGI AppleEvent. theCGIHdl must be valid. */
  1266. static OSErr
  1267. cgiAEComplete ( CGIHdl theCGIHdl )
  1268. {
  1269.     OSErr    theErr;
  1270.     #if kCompileWithDebugLogging
  1271.     char        tempStr[16];
  1272.     #endif
  1273.     
  1274.     my_assert ( theCGIHdl != NULL, "\pcgiAEComplete: theCGIHdl is NULL" );
  1275.     my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
  1276.         "\pcgiAEComplete: theCGIHdl is already locked!" );
  1277.     HLock ( (Handle)theCGIHdl );
  1278.     
  1279.     if ( (*theCGIHdl)->responseData != NULL )
  1280.     {
  1281.         #if kCompileWithCGIResponseDataAsHandle
  1282.         my_assert ( (HGetState((Handle)((*theCGIHdl)->responseData)) & kMemoryHandleLockedFlag) == nil,
  1283.             "\pcgiAEComplete: (*theCGIHdl)->responseData is already locked!" );
  1284.         HLock ( (*theCGIHdl)->responseData );
  1285.         #endif
  1286.         
  1287.         /* If the user's "CustomCGIProcess" function set the responseData properly,
  1288.             return it. */
  1289.         theErr = AEPutParamPtr ( &((*theCGIHdl)->replyEvent), keyDirectObject, typeChar,
  1290.             #if kCompileWithCGIResponseDataAsHandle
  1291.                 *((*theCGIHdl)->responseData),
  1292.             #else
  1293.                 (Ptr)((*theCGIHdl)->responseData),
  1294.             #endif
  1295.             (*theCGIHdl)->responseSize );
  1296.             
  1297.         #if kCompileWithCGIResponseDataAsHandle
  1298.         HUnlock ( (*theCGIHdl)->responseData );
  1299.         #endif
  1300.     }
  1301.     else
  1302.     {
  1303.         /* if the user's "CustomCGIProcess" failed to set the responseData properly,
  1304.             return an error header. */
  1305.         theErr = AEPutParamPtr ( &((*theCGIHdl)->replyEvent), keyDirectObject, typeChar,
  1306.             (Ptr)gHTTPHeaderErr, gHTTPHeaderErrSize );
  1307.     }
  1308.     
  1309.     HUnlock ( (Handle)theCGIHdl );
  1310.     
  1311.     /* give time, then cgiPostProcess */
  1312.     ProcessGiveTime ( nil );
  1313.     cgiPostProcess ( theCGIHdl );
  1314.     
  1315.     #if kCompileWithDebugLogging
  1316.     CGILogData ( theCGIHdl );
  1317.     #endif
  1318.     
  1319.     /* deallocate memory */
  1320.     cgiDisposeHandle ( theCGIHdl );
  1321.         
  1322.     #if kCompileWithDebugLogging
  1323.     LogStringP ( "\p___________________\rCGI Event Completed\rTICKS: " );
  1324.     sprintf ( tempStr, "%d", TickCount() );
  1325.     LogStringBreak ( tempStr );
  1326.     LogStringBreakP ( "\p===================" );
  1327.     #endif
  1328.     
  1329.     /* we're done this CGI event, so we're less busy now */
  1330.     ProcessIsLessBusy ();
  1331.     
  1332.     return theErr;
  1333. } /* cgiAEComplete */
  1334.  
  1335.  
  1336. /*  */
  1337. static void
  1338. cgiPostProcess ( CGIHdl theCGIHdl )
  1339. {
  1340.     CustomCGIPostProcess ( theCGIHdl );
  1341. } /* cgiPostProcess */
  1342.  
  1343.  
  1344. #if kCompileWithCGImethod
  1345. #pragma segment AppleEvents
  1346. /* private function to get an HTTPMethod from an AppleEvent parameter */
  1347. static OSErr
  1348. cgiAEGetParamHTTPMethod ( const AppleEvent *theAppleEvent, AEKeyword theAEKeyword, HTTPMethod *theMethod, char *tempBuffer, long bufferSize )
  1349. {
  1350.     OSErr        theErr;
  1351.     DescType    actualType;
  1352.     Size        actualSize;
  1353.     int            stringDiff;
  1354.     
  1355.     my_assert ( theMethod != NULL, "\pcgiAEGetParamHTTPMethod: theMethod ptr is NULL" );
  1356.     my_assert ( theAppleEvent != NULL, "\pcgiAEGetParamHTTPMethod: theAppleEvent ptr is NULL" );
  1357.     my_assert ( tempBuffer != NULL, "\pcgiAEGetParamHTTPMethod: tempBuffer ptr is NULL" );
  1358.     
  1359.     theErr = AEGetParamPtr ( theAppleEvent, theAEKeyword, typeChar,
  1360.         &actualType, (Ptr)tempBuffer, bufferSize, &actualSize );
  1361.     if ( theErr == noErr )
  1362.     {
  1363.         my_assert ( actualSize <= bufferSize,
  1364.             "\pcgiAEGetParamHTTPMethod: actual param size too big" );
  1365.         
  1366.         /* terminate the buffer with a null byte */
  1367.         tempBuffer[actualSize] = nil;
  1368.         
  1369.         /* compare the buffer with constants to determine the http method used */
  1370.         
  1371.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodPost );
  1372.         if ( stringDiff == nil )
  1373.         {
  1374.             *theMethod = HTTP_post;
  1375.         }
  1376.         else
  1377.         {
  1378.             stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGet );
  1379.             if ( stringDiff == nil )
  1380.             {
  1381.                 *theMethod = HTTP_get;
  1382.             }
  1383.             else
  1384.             {
  1385.                 stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGetConditional );
  1386.                 if ( stringDiff == nil )
  1387.                 {
  1388.                     *theMethod = HTTP_getConditional;
  1389.                 }
  1390.                 else
  1391.                 {
  1392.                     *theMethod = HTTP_UNDEFINED;
  1393.                 }
  1394.             }
  1395.         }
  1396.     }
  1397.     
  1398.     return theErr;
  1399. } /* cgiAEGetParamHTTPMethod */
  1400. #endif
  1401.  
  1402.  
  1403. /***  SEND PARTIAL SUPPORT  ***/
  1404. #pragma mark -
  1405.  
  1406. /*    From the WebSTAR 1.2 addendum:
  1407.     
  1408.     An ACGI has to inform the WebSTAR server that it intends to
  1409.     return its results in pieces over a period of time, rather than
  1410.     simply lumping it all into the value returned from the WWWWsdoc
  1411.     event. WebSTAR examines the results from an ACGI's reply to see
  1412.     if it matches the string:
  1413.     
  1414.     <SEND_PARTIAL>
  1415.     
  1416.     If this 14 character string is matched exactly (case sensitive),
  1417.     then WebSTAR doesn’t send anything to the client and keeps the
  1418.     connection open until the timeout period expires or WebSTAR
  1419.     receives data via the Send Partial event for that con-nection.
  1420.     
  1421.     Send Partial events received before the <SEND_PARTIAL> response
  1422.     to the sdoc
  1423.     
  1424.     event are also a legal way to indicate that server push
  1425.     functions are to be performed for a given connection.
  1426.     
  1427.     As long as Send Partial events are received for a given
  1428.     connection, the timeout timer is restarted and the data is sent
  1429.     to the client. If the Send Partial event’s “more” parameter is
  1430.     FALSE, the server closes the connection and assumes that the
  1431.     ACGI has finished sending data.
  1432.     
  1433.     To send events back to WebSTAR from a language like C or Pascal,
  1434.     you must extract and save the “from” Apple event attribute from
  1435.     the reply event sent to your WWWWsdoc handler. The “from” event
  1436.     contains the AEAddressDesc used to address the “SPar” events to
  1437.     WebSTAR.
  1438.     
  1439.     Here's the general flow of events
  1440.     
  1441.     1. A WWW client sends an ACGI URL request to WebSTAR.
  1442.     
  1443.     2. WebSTAR sends a WWWWsdoc event to the ACGI, passing the
  1444.     connection ID.
  1445.     
  1446.     3. The ACGI decides it needs to return data in pieces, so it
  1447.     replies to the sdoc event with the string <SEND_PARTIAL> and
  1448.     saves the connection ID.
  1449.     
  1450.     4. WebSTAR sees that the ACGI wants to return partial data, so
  1451.     it sets a flag indi-cating that the connection should be checked
  1452.     periodically for timeouts and resets the timer.
  1453.     
  1454.     5. The ACGI sends a Send Partial event to WebSTAR with the first
  1455.     chunk of data, the connection ID, and the “more” flag set to
  1456.     TRUE, indicating more data to come.
  1457.     
  1458.     6. WebSTAR handles the Send Partial event, finds the requested
  1459.     connection, and queues up the data for transmission to the
  1460.     client (allowing the thread owning the connection to schedule
  1461.     the transmission.) WebSTAR then resets the time-out for the
  1462.     connection.
  1463.     
  1464.     Steps 5 and 6 repeat at whatever interval the client decides
  1465.     until the “more” parameter is false. WebSTAR receives the final
  1466.     Send Partial event with the “more” flag set to false, sends the
  1467.     accompanying data, and closes the client connection. */
  1468.  
  1469. /*    The connection ID is found in theCGIHdl as the 'connection' field.
  1470.     Connection is used to identify the particular connection on which to send
  1471.     the data.
  1472.     The data to be sent is pointed to by theData, which is dataSize bytes long.
  1473.     If sendMore is true, there is more data to be sent after processing this
  1474.     event. If it is false, the server will terminate the connection after
  1475.     sending the data from this event.
  1476.     theCGIHdl should not be locked when it is passed to this function. 
  1477.     */
  1478. #if kCompileWithCGISendPartial
  1479. #pragma segment AppleEvents
  1480. p_export
  1481. OSErr
  1482. CGIAESendPartial ( CGIHdl theCGIHdl, char *theData, long dataSize, Boolean sendMore )
  1483. {
  1484.     OSErr            theErr;
  1485.     DescType        descApp;
  1486.     AEAddressDesc    targetAddress;
  1487.     AppleEvent        theAppleEvent;
  1488.     AppleEvent        reply;
  1489.     short            errNum;
  1490.     DescType        returnedType;
  1491.     long            actualSize;
  1492.     
  1493.     my_assert ( theCGIHdl != NULL, "\pCGIAESendPartial: theCGIHdl is NULL" );
  1494.     my_assert ( theData != NULL, "\pCGIAESendPartial: theData is NULL" );
  1495.     
  1496.     /* ••• need to change this to use the process ID from the server */
  1497.     /* create the application descriptor */
  1498.     descApp    = kAEClassCGI;
  1499.     theErr    = AECreateDesc ( typeApplSignature, &descApp, sizeof(DescType),
  1500.                 &targetAddress );
  1501.     if ( theErr != noErr )
  1502.     {
  1503.         goto EXIT_AESEND;
  1504.     }
  1505.     
  1506.     /* create the apple event record */
  1507.     theErr = AECreateAppleEvent ( kAEClassCGI, kMyAESendPartial, &targetAddress,
  1508.                 kAutoGenerateReturnID, kAnyTransactionID, &theAppleEvent );
  1509.     if ( theErr != noErr )
  1510.     {
  1511.         goto EXIT_AESEND;
  1512.     }
  1513.     
  1514.     /* put the data */
  1515.     theErr = AEPutParamPtr ( &theAppleEvent, kCGIPartialData, typeChar,
  1516.         theData, dataSize );
  1517.     if ( theErr != noErr )
  1518.     {
  1519.         goto EXIT_AESEND;
  1520.     }
  1521.     
  1522.     my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
  1523.         "\pCGIAESendPartial: theCGIHdl is already locked!" );
  1524.     HLockHi ( (Handle)theCGIHdl );
  1525.     /* put the connection id */
  1526.     theErr = AEPutParamPtr ( &theAppleEvent, kConnectionIDKeyword, typeLongInteger,
  1527.                 &((*theCGIHdl)->connection), sizeof(long) );
  1528.     if ( theErr != noErr )
  1529.     {
  1530.         goto EXIT_AESEND;
  1531.     }
  1532.     HUnlock ( (Handle)theCGIHdl );
  1533.     
  1534.     /* put the more value */
  1535.     theErr = AEPutParamPtr ( &theAppleEvent, kMoreKeyword, typeBoolean, &sendMore,
  1536.             sizeof(Boolean) );
  1537.     if ( theErr != noErr )
  1538.     {
  1539.         goto EXIT_AESEND;
  1540.     }
  1541.     
  1542.     /* send the apple event */
  1543.     theErr = AESend ( &theAppleEvent, &reply, kAEWaitReply + kAENeverInteract /*5-14*/,
  1544.                 kAENormalPriority, kAEMyTimeoutInTicks, gAEIdleUPP, NULL );
  1545.     if ( theErr != noErr )
  1546.     {
  1547.         goto EXIT_AESEND;
  1548.     }
  1549.     
  1550.     /* extract the error reply */
  1551.     errNum    = noErr;
  1552.     theErr    = AEGetParamPtr ( &reply, keyErrorNumber, typeSMInt, &returnedType, &errNum,
  1553.                 sizeof(errNum), &actualSize );
  1554.     if ( (theErr != noErr) && (theErr != errAEDescNotFound) )
  1555.     {
  1556.         goto EXIT_AESEND;
  1557.     }
  1558.     
  1559.     theErr = errNum;
  1560.  
  1561.     EXIT_AESEND:
  1562.     
  1563.     AEDisposeDesc ( &targetAddress );
  1564.     AEDisposeDesc ( &theAppleEvent );
  1565.     
  1566.     return theErr;
  1567. } /* CGIAESendPartial */
  1568. #endif    /* kCompileWithCGISendPartial */
  1569.  
  1570.  
  1571. /** LOGGING **/
  1572. #pragma mark -
  1573.  
  1574. /*  */
  1575. p_export
  1576. void
  1577. CGILogData ( CGIHdl theCGIHdl )
  1578. {
  1579.     #if kCompileWithCGIserver_port || kCompileWithCGISendPartial || kCompileWithCGIFormHandling
  1580.     char    tempStr[256];
  1581.     #endif
  1582.     #if kCompileWithCGIFormHandling
  1583.     long    counter;
  1584.     #endif
  1585.     
  1586.     LogStringBreakP ( "\p=== CGI PARAMETERS ===" );
  1587.     
  1588.     #if kCompileWithCGIpath_args
  1589.     if ( (*theCGIHdl)->path_args != NULL )
  1590.     {
  1591.         LogString ( "path_args:" );
  1592.         LogStringBreak ( (*theCGIHdl)->path_args );
  1593.     }
  1594.     #endif
  1595.     
  1596.     #if kCompileWithCGIhttp_search_args
  1597.     if ( (*theCGIHdl)->http_search_args != NULL )
  1598.     {
  1599.         LogString ( "http_search_args:" );
  1600.         LogStringBreak ( (*theCGIHdl)->http_search_args );
  1601.     }
  1602.     #endif
  1603.     
  1604.     #if kCompileWithCGIusername
  1605.     LogString ( "username:" );
  1606.     LogStringBreak ( (*theCGIHdl)->username );
  1607.     #endif
  1608.     
  1609.     #if kCompileWithCGIpassword
  1610.     if ( (*theCGIHdl)->password != NULL )
  1611.     {
  1612.         LogString ( "password:" );
  1613.         LogStringBreak ( (*theCGIHdl)->password );
  1614.     }
  1615.     #endif
  1616.     
  1617.     #if kCompileWithCGIfrom_user
  1618.     if ( (*theCGIHdl)->from_user != NULL )
  1619.     {
  1620.         LogString ( "from_user:" );
  1621.         LogStringBreak ( (*theCGIHdl)->from_user );
  1622.     }
  1623.     #endif
  1624.     
  1625.     #if kCompileWithCGIclient_address
  1626.     if ( (*theCGIHdl)->client_address != NULL )
  1627.     {
  1628.         LogString ( "client_address:" );
  1629.         LogStringBreak ( (*theCGIHdl)->client_address );
  1630.     }
  1631.     #endif
  1632.     
  1633.     #if kCompileWithCGIpost_args
  1634.     if ( (*theCGIHdl)->post_args != NULL )
  1635.     {
  1636.         LogString ( "post_args:" );
  1637.         LogStringBreak ( (*theCGIHdl)->post_args );
  1638.     }
  1639.     #endif
  1640.     
  1641.     #if kCompileWithCGImethod
  1642.     LogString ( "method:" );
  1643.     switch ( (*theCGIHdl)->method )
  1644.     {
  1645.         case HTTP_get :
  1646.             LogStringBreak ( kCGIHTTPMethodGet );
  1647.             break;
  1648.         
  1649.         case HTTP_post :
  1650.             LogStringBreak ( kCGIHTTPMethodPost );
  1651.             break;
  1652.         
  1653.         case HTTP_getConditional :
  1654.             LogStringBreak ( kCGIHTTPMethodGetConditional );
  1655.             break;
  1656.         
  1657.         default :
  1658.             LogStringBreak ( "<undefined>" );
  1659.             break;    
  1660.     }
  1661.     #endif
  1662.     
  1663.     #if kCompileWithCGIserver_name
  1664.     if ( (*theCGIHdl)->server_name != NULL )
  1665.     {
  1666.         LogString ( "server_name:" );
  1667.         LogStringBreak ( (*theCGIHdl)->server_name );
  1668.     }
  1669.     #endif
  1670.     
  1671.     #if kCompileWithCGIserver_port
  1672.     LogString ( "server_port:" );
  1673.     sprintf ( tempStr, "%d", (*theCGIHdl)->server_port );
  1674.     LogStringBreak ( tempStr );
  1675.     #endif
  1676.     
  1677.     #if kCompileWithCGIscript_name
  1678.     if ( (*theCGIHdl)->script_name != NULL )
  1679.     {
  1680.         LogString ( "script_name:" );
  1681.         LogStringBreak ( (*theCGIHdl)->script_name );
  1682.     }
  1683.     #endif
  1684.     
  1685.     #if kCompileWithCGIcontent_type
  1686.     if ( (*theCGIHdl)->content_type != NULL )
  1687.     {
  1688.         LogString ( "content_type:" );
  1689.         LogStringBreak ( (*theCGIHdl)->content_type );
  1690.     }
  1691.     #endif
  1692.     
  1693.     #if kCompileWithCGIreferer
  1694.     if ( (*theCGIHdl)->referer != NULL )
  1695.     {
  1696.         LogString ( "referer:" );
  1697.         LogStringBreak ( (*theCGIHdl)->referer );
  1698.     }
  1699.     #endif
  1700.     
  1701.     #if kCompileWithCGIuser_agent
  1702.     if ( (*theCGIHdl)->user_agent != NULL )
  1703.     {
  1704.         LogString ( "user_agent:" );
  1705.         LogStringBreak ( (*theCGIHdl)->user_agent );
  1706.     }
  1707.     #endif
  1708.     
  1709.     #if kCompileWithCGIActionSupport
  1710.     LogString ( "action:" );
  1711.     LogStringBreak ( (*theCGIHdl)->action );
  1712.     if ( (*theCGIHdl)->action_path != NULL )
  1713.     {
  1714.         LogString ( "action_path:" );
  1715.         LogStringBreak ( (*theCGIHdl)->action_path );
  1716.     }
  1717.     #endif
  1718.     
  1719.     #if kCompileWithCGIclient_ip
  1720.     LogString ( "client_ip:" );
  1721.     LogStringBreak ( (*theCGIHdl)->client_ip );
  1722.     #endif
  1723.     
  1724.     #if kCompileWithCGIfull_request
  1725.     if ( (*theCGIHdl)->full_request != NULL )
  1726.     {
  1727.         LogString ( "full_request:" );
  1728.         LogStringBreak ( (*theCGIHdl)->full_request );
  1729.     }
  1730.     #endif
  1731.     
  1732.     #if kCompileWithCGISendPartial
  1733.     LogString ( "connection:" );
  1734.     sprintf ( tempStr, "%d", (*theCGIHdl)->connection );
  1735.     LogStringBreak ( tempStr );
  1736.     #endif
  1737.     
  1738.     #if kCompileWithCGIFormHandling
  1739.     LogString ( "totalFields:" );
  1740.     sprintf ( tempStr, "%d", (*theCGIHdl)->totalFields );
  1741.     LogStringBreak ( tempStr );
  1742.     
  1743.     if ( (*theCGIHdl)->totalFields > nil )
  1744.     {
  1745.         LogStringBreak ( "formFields:" );
  1746.         for ( counter = 0; counter < (*theCGIHdl)->totalFields; counter++ )
  1747.         {
  1748.             LogStringP ( "\p\t" );    /* put a tab character at the begining */
  1749.             LogStringAndSeparator ( (((*theCGIHdl)->formFields)[counter]).name, '\t' );
  1750.             LogStringBreak ( (((*theCGIHdl)->formFields)[counter]).value );
  1751.         }
  1752.     }
  1753.     #endif
  1754.     
  1755.     LogStringBreakP ( "\p=== end cgi parameters ===" );
  1756.     
  1757. } /* CGILogData */
  1758.  
  1759.  
  1760. #endif    /* kCompileWithCGICode */
  1761.  
  1762. /***  EOF  ***/
  1763.